package com.jarveis.frame.redis;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.exceptions.JedisConnectionException;

import com.jarveis.frame.io.Resource;
import com.jarveis.frame.jdbc.DataSourceWrapper;
import com.jarveis.frame.lang.CharacterUtil;
import com.jarveis.frame.lang.NumberUtil;
import com.jarveis.frame.lang.StringUtil;

/**
 * Redis配置类
 * 
 * @author liuguojun
 */
public class RedisManager {

	private static Logger logger = Logger.getLogger(RedisManager.class);
	private static HashMap<String, DataSourceWrapper> dataSources = new HashMap<String, DataSourceWrapper>();
	private static DataSourceWrapper dataSourceWrapper;
	private static final ThreadLocal<DataSourceWrapper> dsThreadLocal = new ThreadLocal<DataSourceWrapper>();
	private static final ThreadLocal<Jedis> cnThreadLocal = new ThreadLocal<Jedis>();

	public static final String CONFIG = "config.xml";

	/**
	 * 初始化配置
	 */
	public static void init() {
		init(CONFIG);
	}

	/**
	 * 初始化配置
	 * 
	 * @param filePath
	 */
	public static void init(String filePath) {
		try {
			parseConfig(filePath);
		} catch (Exception ex) {
			logger.error("加载数据源出错！", ex);
		}
	}

	/**
	 * 设置配置文件
	 * 
	 * @param fileName
	 * @return Properties
	 * @throws Exception
	 */
	private static void parseConfig(String filePath) throws Exception {
		Document document = Jsoup.parse(Resource.getStream(filePath),
				CharacterUtil.UTF8, "", Parser.xmlParser());
		Elements dss = document.select("redisConfig > datasource");
		for (Element ds : dss) {
			parseDataSource(ds);
		}
	}

	/**
	 * 解析数据源
	 * 
	 * @param element
	 * @throws Exception
	 */
	private static void parseDataSource(Element element) throws Exception {
		Properties prop = new Properties();

		String id = element.attr("id");
		String df = element.attr("default");
		Elements pps = element.select("property");
		for (Element pp : pps) {
			String name = pp.attr("name");
			String value = pp.attr("value");
			prop.put(name, value);
		}

		int maxTotal = NumberUtil.parseInt(prop.getProperty("maxTotal"));
		if (maxTotal < 1) {
			maxTotal = 100;
		}
		int maxIdle = NumberUtil.parseInt(prop.getProperty("maxIdle"));
		if (maxIdle < 1) {
			maxIdle = 20;
		}
		int maxWait = NumberUtil.parseInt(prop.getProperty("maxWait"));
		if (maxWait < 1) {
			maxWait = 80;
		}
		boolean testOnBorrow = Boolean
				.valueOf(prop.getProperty("testOnBorrow"));
		boolean testOnReturn = Boolean
				.valueOf(prop.getProperty("testOnReturn"));
		String ip = prop.getProperty("ip");
		if (ip == null) {
			ip = "127.0.0.1";
		}
		int port = NumberUtil.parseInt(prop.getProperty("port"));
		if (port < 1) {
			port = 6379;
		}
		int timeout = NumberUtil.parseInt(prop.getProperty("timeout"));
		if (timeout < 1) {
			timeout = 2000;
		}
		String password = prop.getProperty("password");

		JedisPoolConfig config = new JedisPoolConfig();
		config.setMaxTotal(maxTotal);
		config.setMaxIdle(maxIdle);
		config.setMaxWaitMillis(maxWait);
		config.setTestOnBorrow(testOnBorrow);
		config.setTestOnReturn(testOnReturn);
		JedisPool pool = null;
		if (StringUtil.isEmpty(password)) {
			pool = new JedisPool(config, ip, port, timeout);
		} else {
			pool = new JedisPool(config, ip, port, timeout, password);
		}

		DataSourceWrapper dsw = new DataSourceWrapper();
		dsw.setId(id);
		dsw.setDf(df == null ? false : Boolean.parseBoolean(df));
		dsw.setDataSource(pool);

		if (dsw.getDf()) {
			dataSourceWrapper = dsw;
		}

		dataSources.put(dsw.getId(), dsw);
	}

	/**
	 * 返回当前线程使用的数据源
	 * 
	 * @return DataSource
	 * @throws Exception
	 */
	public static synchronized JedisPool getDataSource() throws Exception {
		DataSourceWrapper dsw = dsThreadLocal.get();
		if (dsw == null) {
			if (dataSourceWrapper != null) {
				dsw = dataSourceWrapper;
				dsThreadLocal.set(dsw);
			} else {
				Exception ex = new Exception("数据源不能被发现, 请检查相关配置或是否初始化配置");
				logger.error(ex.getMessage(), ex);
				throw ex;
			}
		}
		return (JedisPool) dsw.getDataSource();
	}

	/**
	 * 改变当前线程使用的数据源
	 * 
	 * @param key
	 * @return DataSource
	 * @throws SQLException
	 */
	public static synchronized void changeDataSource(String key)
			throws SQLException {
		DataSourceWrapper dsw = dataSources.get(key);
		if (dsw == null) {
			throw new SQLException(key + "数据源不能够被发现");
		}
		dsThreadLocal.set(dsw);
	}

	/**
	 * 获取数据库连接
	 * 
	 * @return Connection
	 */
	public static synchronized Jedis getConnection() {
		Jedis conn = null;

		try {
			conn = cnThreadLocal.get();
			if (conn == null) {
				conn = getDataSource().getResource();
				cnThreadLocal.set(conn);
			}
		} catch (JedisConnectionException ex) {
			logger.error("请检查数据的连接配置(maxIdle)", ex);
		} catch (Exception ex) {
			logger.error("获取数据库连接出错", ex);
		}

		return conn;
	}

	/**
	 * 关闭数据库连接
	 * 
	 * @return
	 */
	public static synchronized void closeConnection() {
		Jedis conn = cnThreadLocal.get();

		try {
			if (conn != null && conn.isConnected()) {
				conn.close();
				cnThreadLocal.remove();
			}
		} catch (Exception ex) {
			logger.error("获取数据库连接出错", ex);
		}
	}
}
